import { DeviceConnectionManager } from './deviceConnectionManager';

/**
 * Raw REPL 管理器
 * 实现 MicroPython Raw REPL 协议，用于程序化控制设备
 * 参考：Thonny, mpremote, rshell 的实现方式
 */
export class RawREPLManager {
    private connectionManager: DeviceConnectionManager;
    private isInRawMode: boolean = false;

    constructor(connectionManager: DeviceConnectionManager) {
        this.connectionManager = connectionManager;
    }

    /**
     * 进入 Raw REPL 模式
     * 参考 Thonny 的实现，先重置设备状态再进入 Raw REPL
     */
    public async enterRawREPL(): Promise<void> {
        if (this.isInRawMode) {
            console.log('Already in raw REPL mode');
            return;
        }

        console.log('Entering raw REPL mode...');

        // 1. 多次发送 Ctrl+C 中断任何运行中的程序
        console.log('Sending Ctrl+C to interrupt...');
        await this.connectionManager.sendRawData(Buffer.from([0x03, 0x03]));
        await this.drainBuffer(100);

        // 2. 发送 Ctrl+D 退出可能存在的粘贴模式或结束当前输入
        console.log('Sending Ctrl+D to exit paste mode...');
        await this.connectionManager.sendRawData(Buffer.from([0x04]));
        await this.drainBuffer(100);

        // 3. 再发送一次 Ctrl+C 确保在 Normal REPL 状态
        await this.connectionManager.sendRawData(Buffer.from([0x03]));
        await this.drainBuffer(100);

        // 4. 发送 Ctrl+B 确保退出任何 Raw REPL 模式
        console.log('Sending Ctrl+B to exit raw REPL if present...');
        await this.connectionManager.sendRawData(Buffer.from([0x02]));
        await this.drainBuffer(100);

        // 5. 最后发送 Ctrl+A 进入 Raw REPL
        console.log('Sending Ctrl+A to enter raw REPL...');
        await this.connectionManager.sendRawData(Buffer.from([0x01]));

        // 6. 等待确认消息 "raw REPL; CTRL-B to exit\r\n>"
        console.log('Waiting for raw REPL confirmation...');
        const response = await this.readUntil(Buffer.from('>'), 5000);

        if (!response.includes('raw REPL')) {
            throw new Error(`Failed to enter raw REPL mode. Response: ${response.substring(0, 100)}`);
        }

        this.isInRawMode = true;
        console.log('Successfully entered raw REPL mode');
    }

    /**
     * 退出 Raw REPL 模式，返回 Normal REPL
     */
    public async exitRawREPL(): Promise<void> {
        if (!this.isInRawMode) {
            return;
        }

        console.log('Exiting raw REPL mode...');

        // 发送 Ctrl+B 退出 raw REPL
        await this.connectionManager.sendRawData(Buffer.from([0x02]));
        await this.delay(100);

        this.isInRawMode = false;
        console.log('Exited raw REPL mode');
    }

    /**
     * 在 Raw REPL 模式下执行代码
     * @returns [stdout, stderr] - 标准输出和错误输出
     */
    public async execute(code: string): Promise<[string, string]> {
        if (!this.isInRawMode) {
            throw new Error('Not in raw REPL mode. Call enterRawREPL() first.');
        }

        console.log('Executing code in raw REPL...');

        // 1. 发送代码
        await this.connectionManager.sendRawData(Buffer.from(code, 'utf-8'));

        // 2. 发送 Ctrl+D 表示代码结束
        await this.connectionManager.sendRawData(Buffer.from([0x04]));

        // 3. 等待 "OK" 确认（后面可能跟着回显的数据）
        const okResponse = await this.readUntil(Buffer.from('OK'), 10000);

        if (!okResponse.includes('OK')) {
            throw new Error('Failed to get OK confirmation from device');
        }

        console.log('Code sent, waiting for execution results...');

        // 4. 读取 stdout（直到 0x04）
        const stdoutBuffer = await this.readUntil(Buffer.from([0x04]), 30000);
        const stdout = stdoutBuffer.slice(0, -1); // 去掉结尾的 0x04

        // 5. 读取 stderr（直到 0x04）
        const stderrBuffer = await this.readUntil(Buffer.from([0x04]), 5000);
        const stderr = stderrBuffer.slice(0, -1); // 去掉结尾的 0x04

        console.log('Execution completed');
        console.log('stdout length:', stdout.length);
        console.log('stderr length:', stderr.length);

        return [stdout, stderr];
    }

    /**
     * 执行代码的便捷方法，自动进入和退出 Raw REPL
     */
    public async executeAndReturn(code: string): Promise<[string, string]> {
        try {
            await this.enterRawREPL();
            return await this.execute(code);
        } finally {
            await this.exitRawREPL();
        }
    }

    /**
     * 检测当前设备处于哪种 REPL 模式
     */
    public async detectREPLMode(): Promise<'normal' | 'raw' | 'paste' | 'unknown'> {
        console.log('Detecting REPL mode...');

        // 发送一个回车，看返回什么提示符
        await this.connectionManager.sendRawData(Buffer.from('\r\n'));
        await this.delay(200);

        return new Promise((resolve) => {
            let dataBuffer = Buffer.alloc(0);
            const timeout = setTimeout(() => {
                disposable.dispose();
                const responseData = dataBuffer.toString('utf-8');
                console.log('Detection timeout, response:', responseData);
                resolve('unknown');
            }, 2000);

            const disposable = this.connectionManager.onRawDataReceived((chunk: Buffer) => {
                dataBuffer = Buffer.concat([dataBuffer, chunk]);
                const responseData = dataBuffer.toString('utf-8');

                // 检查 Raw REPL 提示符
                if (responseData.includes('raw REPL') || />\s*$/.test(responseData)) {
                    clearTimeout(timeout);
                    disposable.dispose();
                    console.log('Detected: Raw REPL mode');
                    this.isInRawMode = true;
                    resolve('raw');
                    return;
                }

                // 检查 Paste Mode 提示符
                if (responseData.includes('===') || responseData.includes('paste mode')) {
                    clearTimeout(timeout);
                    disposable.dispose();
                    console.log('Detected: Paste mode');
                    resolve('paste');
                    return;
                }

                // 检查 Normal REPL 提示符
                if (responseData.includes('>>> ')) {
                    clearTimeout(timeout);
                    disposable.dispose();
                    console.log('Detected: Normal REPL mode');
                    this.isInRawMode = false;
                    resolve('normal');
                    return;
                }
            });
        });
    }

    /**
     * 强制回到 Normal REPL 模式
     * 清理所有可能的状态（运行中的程序、粘贴模式、Raw REPL）
     */
    public async resetToNormalREPL(): Promise<void> {
        console.log('Resetting to normal REPL...');

        // 1. 发送多次 Ctrl+C 中断任何运行中的程序
        console.log('Sending Ctrl+C to interrupt...');
        for (let i = 0; i < 2; i++) {
            await this.connectionManager.sendRawData(Buffer.from([0x03]));
            await this.delay(50);
        }

        // 2. 发送 Ctrl+D 退出可能的粘贴模式
        console.log('Sending Ctrl+D to exit paste mode...');
        await this.connectionManager.sendRawData(Buffer.from([0x04]));
        await this.delay(100);

        // 3. 发送 Ctrl+B 退出可能的 Raw REPL
        console.log('Sending Ctrl+B to exit raw REPL...');
        await this.connectionManager.sendRawData(Buffer.from([0x02]));
        await this.delay(100);

        // 4. 最后再发送一次 Ctrl+C 确保在 Normal REPL
        await this.connectionManager.sendRawData(Buffer.from([0x03]));
        await this.delay(100);

        this.isInRawMode = false;
        console.log('Reset to normal REPL completed');
    }

    /**
     * 读取数据直到遇到指定的标记
     */
    private async readUntil(marker: Buffer, timeoutMs: number = 10000): Promise<string> {
        return new Promise((resolve, reject) => {
            let dataBuffer = Buffer.alloc(0);

            const timeout = setTimeout(() => {
                disposable.dispose();
                const preview = dataBuffer.toString('utf-8', 0, Math.min(200, dataBuffer.length));
                const hex = dataBuffer.slice(0, Math.min(50, dataBuffer.length)).toString('hex');
                reject(new Error(`Timeout waiting for marker. Received ${dataBuffer.length} bytes.\nPreview: ${preview}\nHex: ${hex}`));
            }, timeoutMs);

            const disposable = this.connectionManager.onRawDataReceived((chunk: Buffer) => {
                // 直接拼接二进制数据
                dataBuffer = Buffer.concat([dataBuffer, chunk]);

                // 检查是否包含标记
                if (dataBuffer.indexOf(marker) !== -1) {
                    clearTimeout(timeout);
                    disposable.dispose();
                    resolve(dataBuffer.toString('utf-8'));
                }
            });
        });
    }

    /**
     * 清空接收缓冲区（丢弃所有待处理的数据）
     */
    private async drainBuffer(timeMs: number = 200): Promise<void> {
        return new Promise((resolve) => {
            let buffer = Buffer.alloc(0);
            const disposable = this.connectionManager.onRawDataReceived((chunk: Buffer) => {
                buffer = Buffer.concat([buffer, chunk]);
            });

            setTimeout(() => {
                disposable.dispose();
                if (buffer.length > 0) {
                    console.log(`Drained ${buffer.length} bytes from buffer`);
                }
                resolve();
            }, timeMs);
        });
    }

    /**
     * 延迟工具方法
     */
    private delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }

    /**
     * 清理资源
     */
    public dispose(): void {
        if (this.isInRawMode) {
            this.exitRawREPL().catch(err => {
                console.error('Error exiting raw REPL:', err);
            });
        }
    }
}
